package xyz.gzdxc.common.util;

import org.apache.axis.encoding.Base64;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import xyz.gzdxc.common.mysql.entity.CaptchaEntity;

import javax.imageio.ImageIO;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.util.Random;

public class CheckCodeUtil {
    private static final Logger LOG = LoggerFactory.getLogger(CheckCodeUtil.class);

    /**
     *optional character: upper case
     */
    private static final String UPPER_CASE_CODE = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";

    /**
     * optional character: lower case
     */
    private static final String LOWERCASE_CASE_CODE = "abcdefghijklmnopqrstuvwxyz";

    /**
     * optional number
     */
    private static final String NUMBER_CODE = "0123456789";

    /**
     * font style
     */
    private static final String[] fontNames = {"宋体", "华文楷体", "黑体", "微软雅黑", "楷体_GB2312"};

    /**
     * picture width
     */
    private static final int pic_width = 90;

    /**
     * picture height
     */
    private static final int pic_height = 35;

    /**
     * the random generator
     */
    private static final Random random = new Random();

    private static final String NUMBER_ONE = "1";
    private static final String UPPER_CASE_A = "A";
    private static final String LOWER_CASE_A = "a";

    /**
     * background color
     */
    private final Color backgroundColor = new Color(255, 255, 255);

    /**
     * check code text
     */
    private String text;

    /**
     * save the picture to the specific output stream
     */
    public static void output(BufferedImage image, OutputStream out) throws IOException {
        ImageIO.write(image, "PNG", out);
    }

    /**
     * generate random color
     *
     * @return
     */
    private Color randomColor(){
        int red = random.nextInt(150);
        int green = random.nextInt(150);
        int blue = random.nextInt(150);
        return new Color(red, green, blue);
    }

    /**
     * generate random font style
     *
     * @return
     */
    private Font randomFont(){
        int index = random.nextInt(fontNames.length);
        /**
         * generate random font name
         */
        String fontName = fontNames[index];
        /**
         * generate random style, 0(no style), 1(bold), 2(italic), 3(bold + italic)
         */
        int style = random.nextInt(4);
        /**
         * generate random number
         */
        int size = random.nextInt(5) + 24;
        return new Font(fontName, style, size);
    }

    /**
     * draw disturb line
     *
     * @param image
     */
    private void drawLine(BufferedImage image){
        // total draw 3 line
        int num = 3;
        LOG.info("line num => {}", num);
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        for(int i=0; i<num; i++){
            //generate 2 positions of dots, that is 4 values
            int x1 = random.nextInt(pic_width);
            int y1 = random.nextInt(pic_height);
            int x2 = random.nextInt(pic_width);
            int y2 = random.nextInt(pic_height);
            g2.setStroke(new BasicStroke(1.5F));
            // the disturb line is blue
            g2.setColor(Color.BLUE);
            // draw the line
            g2.drawLine(x1, y1, x2, y2);
        }
    }

    /**
     * random generate a character
     *
     * @param codes
     * @return
     */
    private char randomChar(String codes){
        int index = random.nextInt(codes.length());
        return codes.charAt(index);
    }

    /**
     * create BufferedImage
     */
    private BufferedImage createImage(){
        BufferedImage image = new BufferedImage(pic_width, pic_height, BufferedImage.TYPE_INT_RGB);
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        g2.setColor(this.backgroundColor);
        g2.fillRect(0, 0, pic_width, pic_height);
        return image;
    }

    /**
     * call this method to get the check code
     *
     * @return
     * @throws IOException
     */
    public CaptchaEntity getCheckCodeImage(int captchaLen, String captchaSet) throws IOException{
        // create buffered image
        BufferedImage image = createImage();
        // get the draw environment
        Graphics2D g2 = (Graphics2D) image.getGraphics();
        // be used to store the check code text generated
        StringBuilder sb = new StringBuilder();
        // draw 4 character to the picture
        String codes = "";
        if(captchaSet.contains(NUMBER_ONE)){
            codes = NUMBER_CODE;
        }

        if(captchaSet.contains(UPPER_CASE_A)){
            codes += UPPER_CASE_CODE;
        }

        if(captchaSet.contains(LOWER_CASE_A)){
            codes += LOWERCASE_CASE_CODE;
        }

        LOG.info("codes => {}", codes);

        //loop captchaLen times, generate a character for each time
        for(int i = 0; i < captchaLen; i++){
            //random generate a character
            String s = randomChar(codes) + "";
            //add the character to sb
            sb.append(s);
            // set x axis coordinate of current character
            float x = i * 1.0F * pic_width / captchaLen;
            //set random font
            g2.setFont(randomFont());
            //set random color
            g2.setColor(randomColor());
            //draw
            g2.drawString(s, x, pic_height - 5);
        }
        //set the generated string to this.text
        this.text = sb.toString();
        LOG.info("this.text => {}", this.text);
        //draw the disturb line
        drawLine(image);
        CaptchaEntity captcha = new CaptchaEntity();
        //output stream
        ByteArrayOutputStream stream = new ByteArrayOutputStream();
        ImageIO.write(image, "png", stream);
        String base64 = Base64.encode(stream.toByteArray());
        String imgBase = "data:image/jpg;base64," + base64;
        LOG.info("imgBase => {}", imgBase);
        captcha.setBaseCode(imgBase);
        captcha.setCode(this.text);
        return captcha;
    }

}
